const state = {
	prefix: '_s_n_i_d_',
	sid: new Date().getTime(),
	sCounter: new Date().getTime(),
	domMap: null,
	appVueEle: null,
	serviceRefs: null,
};

let eventDriver = null;

const getSeqIdKey = () => {
	return state.prefix + state.sid;

};

const genOneSeqId = () => {
	state.sCounter = state.sCounter + 1;
	return state.sid + state.sCounter;

};

const buildNodesMap = () => {
	state.domMap = traverseNodes([document]);

};

const refreshNodesMap = (updates) => {
	let nodeUpdates = updates.nodeUpdates;
	if(nodeUpdates){
		//console.log('before nodes update: ' + Object.keys(state.domMap).length);
		let domMap = state.domMap;
		let added = null == nodeUpdates.added ? {} : nodeUpdates.added;
		let removed = null == nodeUpdates.removed ? {} : nodeUpdates.removed;
		for(let key in added){
			domMap[key] = added[key];
		}
		for(let key in removed){
			delete domMap[key];
		}
		//console.log('after nodes update: ' + Object.keys(state.domMap).length);
	}

};

const buildNodesSnapshot = (nodesMap) => {
	//console.log('start build nodes snapshot: ' + Object.keys(nodesMap).length);
	//console.log(nodesMap);
	let snapshotMap = {};
	let idKey = getSeqIdKey();
	for(let key in nodesMap){
		let nodeInfo = nodesMap[key];
		let nodeRef = nodeInfo.node;
		if(null == nodeRef){
			console.log('node not exists: ' + key);
			continue;
		}
		let nodeSnapshot = getNodeSnapshot(idKey, nodeRef)
		if(null == nodeSnapshot.nodeName || '' == nodeSnapshot.nodeName){
			console.log('empty node name');
			console.log(nodeRef);
			continue;
		}
		snapshotMap[key] = nodeSnapshot;
	}
	//console.log('build nodes snapshot finish: ' + Object.keys(snapshotMap).length);
	return snapshotMap;
	
};

const getNodeSnapshot = (idKey, nodeRef) => {
	let parentRef = nodeRef.parentNode;
	let nextSiblingRef = nodeRef.nextSibling;
	let previousSiblingRef = nodeRef.previousSibling;
	let nodeSnapshot = {
		id: nodeRef[idKey],
		pid: null != parentRef ? parentRef[idKey] : 0,
		psid: null != previousSiblingRef ? previousSiblingRef[idKey] : 0,
		nsid: null != nextSiblingRef ? nextSiblingRef[idKey] : 0,
		nodeType: nodeRef.nodeType,
		nodeName: nodeRef.nodeName,
		nodeValue: nodeRef.nodeValue,
	}
	return nodeSnapshot;

};

const traverseNodes = (nodes) => {
	//console.log('start traverse notes, origins: ' + nodes.length);
	let nodesMap = {};
	let seqIdKey = getSeqIdKey();
	let appVueEle = state.appVueEle;
	while(nodes.length > 0){
		let nodeRef = nodes.pop();
		if(appVueEle && appVueEle.contains(nodeRef)){
			continue;
		}
		let nodeName = nodeRef.nodeName;
		let refInfo = {
			node: nodeRef, 
			refSeqId: nodeRef[seqIdKey]
		};
		if(null == refInfo.refSeqId){
			refInfo.refSeqId = genOneSeqId();
			nodeRef[seqIdKey] = refInfo.refSeqId;
		}
		if(null == nodesMap[refInfo.refSeqId]){
			nodesMap[refInfo.refSeqId] = refInfo;
		}
		if('iframe' == nodeName.toLowerCase()){
			let iframeDoc = nodeRef.contentDocument;
			//console.log(nodeRef);
			//console.log(iframeDoc);
			if(null != iframeDoc){
				nodes.unshift(iframeDoc);
			}
		}
		let childNodes = nodeRef.childNodes;
		for(let i = 0; i < childNodes.length; i++){
			nodes.unshift(childNodes[i]);
		}
	}
	//console.log('nodes traverse finish: ' + Object.keys(nodesMap).length);
	return nodesMap;

};

const handleMutations = (mutations) => {
	let appVueEle = state.appVueEle;
	let mutationsMap = {node: [], attr: [], text: []};
	for(let i = 0; i < mutations.length; i++){
		let mutation = mutations[i];
		if(appVueEle && appVueEle.contains(mutation.target)){
			continue;
		}
		if('childList' == mutation.type){
			mutationsMap.node.push(mutation);
			continue;
		}
		// if('attributes' == mutation.type){
		// 	mutationsMap.attr.push(mutation);
		// 	continue;
		// }
		// if('characterData' == mutation.type){
		// 	mutationsMap.text.push(mutation);
		// 	continue;
		// }
	}

	let domMap = {};
	let snapshotMap = {};
	if(mutationsMap.node.length > 0){
		let nodeUpdates = handelNodeListMutation(mutationsMap.node);
		domMap.nodeUpdates = nodeUpdates.dom;
		snapshotMap.nodeUpdates = nodeUpdates.snapshot;
	}
	// if(mutationsMap.attr.length > 0){
	// 	let attrUpdates = self.handleAttributeListMutation(mutationsMap.attr);
	// 	domMap.attrUpdates = attrUpdates;
	// }
	// if(mutationsMap.text.length > 0){
	// 	let textUpdates = self.handleCharacterListMutation(mutationsMap.text);
	// 	domMap.textUpdates = textUpdates;
	// }

	if(snapshotMap.nodeUpdates){
		refreshNodesMap(domMap);
		eventDriver.emit('dom-update-event', state.domMap);
	}

};

const handelNodeListMutation = (mutations) => {
	//console.log('handle node mutations');
	//console.log(mutations);
	let deltaMap = {added: [], removed: []};
	for(let i = 0; i < mutations.length; i++){
		let mutation = mutations[i];
		Array.from(mutation.addedNodes).forEach(e => deltaMap.added.push(e));
		Array.from(mutation.removedNodes).forEach(e => deltaMap.removed.push(e));
	}
	let addedMap = handleMutationNode(deltaMap.added);
	let removedMap = handleMutationNode(deltaMap.removed);
	let nodeUpdates = {
		dom: {
			added: addedMap.dom, 
			removed: removedMap.dom
		},
		snapshot: {
			added: addedMap.snapshot, 
			removed: removedMap.snapshot
		}
	};
	return nodeUpdates;

};

const handleMutationNode = (nodes) => {
	let tmpNode = null;
	let deltaNodes = [];
	let appVueEle = state.appVueEle;
	for(let i = 0; i < nodes.length; i++){
		tmpNode = nodes[i];
		if(appVueEle && appVueEle.contains(tmpNode)){
			console.log('skip mutation node handle for ');
			console.log(tmpNode);
			continue;
		}
		deltaNodes.push(tmpNode);
	}
	let domMap = traverseNodes(deltaNodes);
	let nodesSnapshot = buildNodesSnapshot(domMap);
	return {dom: domMap, snapshot: nodesSnapshot};

};

const handleAttributeListMutation = (mutations) => {
	console.log('handle attr mutations');
	console.log(mutations);

};

const handleCharacterListMutation = (mutations) => {
	console.log('handle text mutations');
	console.log(mutations);

};

const getSnapshotMap = () => {
	if(!state.domMap){
		buildNodesMap();
	}
	return buildNodesSnapshot(state.domMap);
};

const refreshSnapshotMap = () => {
	buildNodesMap();
	return buildNodesSnapshot(state.domMap);

};

const initSetup = (driver) => {
	eventDriver = driver;
	eventDriver.on('startup', (refs) => state.serviceRefs = refs);
	eventDriver.on('app-mount-event', (ele) => state.appVueEle = ele );
	eventDriver.on('dom-mutations-event', (mutations) => handleMutations(mutations));

};

const getElementByIdKey = (idKey) => {
	return state.domMap[idKey];

};

export default function(driver){
	initSetup(driver);
	return {
		getSeqIdKey,
		getSnapshotMap,
		refreshSnapshotMap,
		getElementByIdKey
		
	};

};;